/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_WEAKPTR_H
#define M_WEAKPTR_H

#ifdef _MSC_VER
#pragma warning(disable:4127)
#endif

#include "refptr.h"

#include <stdexcept>

namespace Engine
{
    template<class, template<class> class> class WeakPtr;

	class WeakUtils
    {
	public:

		template<class S1, template<class> class DP1,class S, template<class> class DP>
		static bool IsEqual(const WeakPtr<S1, DP1>& ptr, const WeakPtr<S, DP>& aptr);

		template<class S1, template<class> class DP1,class S, template<class> class DP>
		static bool IsEqual(const WeakPtr<S1, DP1>& ptr, const RefPtr<S, WeakPtrExtension, DP>& aptr);

		template<class S1, template<class> class DP1,class S, template<class> class DP>
		static void Copy(WeakPtr<S1, DP1>& ptr, const WeakPtr<S, DP>& aptr);

		template<class S1, template<class> class DP1,class S, template<class> class DP>
		static void Copy(WeakPtr<S1, DP1>& ptr, const RefPtr<S, WeakPtrExtension, DP>& aptr);

		template<class S1, template<class> class DP1,class S, template<class> class DP>
		static void UnsafeCopy(WeakPtr<S1, DP1>& ptr, const WeakPtr<S, DP>& aptr);

		template<class S1, template<class> class DP1,class S, template<class> class DP>
		static void UnsafeCopy(WeakPtr<S1, DP1>& ptr, const RefPtr<S, WeakPtrExtension, DP>& aptr);
    };

    /**
	*   Class that represent weak reference for counting pointer.
	*/
    template<class T,template<class> class DeletePolicy = DefaultDelete>
    class WeakPtr
    {
    public:

		friend class WeakUtils;

	    /**
		*   Default constructor.
		*/
        WeakPtr():m_counter(0),m_pointer(0){}

		/**
		*   Copy constructor.
        *   Copy proxy object.
        */		
        WeakPtr(const WeakPtr& rhs)
        {
            WeakUtils::Copy(*this, rhs);
        }

		/**
		*   Generelized copy constructor.
        *   Copy proxy object.
        */
		template<class S>
        WeakPtr(const WeakPtr<S, DeletePolicy>& rhs)
        {
            WeakUtils::Copy(*this, rhs);
        }

		/**
		*   Copy constructor.
        *   Copy proxy object.
        */
		template<class S>
        WeakPtr(const RefPtr<S, WeakPtrExtension, DeletePolicy>& aptr)
        {
            WeakUtils::Copy(*this, aptr);
        }

		/**
		*   Destructor.
        */
        ~WeakPtr()
        {
            Delete();
        }

		/**
		*   Operator assign one pointer to another.
        *   First kill prev value,that assign new.
        */
        WeakPtr<T, DeletePolicy>& operator = (const WeakPtr& rhs)
        {
            if(WeakUtils::IsEqual(*this, rhs))
                return *this;

			Delete();

            WeakUtils::Copy(*this, rhs);

            return *this;
        }

		/**
		*   Operator assign one pointer to another.
        *   First kill prev value,that assign new.
        */
		template<class S>
        WeakPtr<T, DeletePolicy>& operator = (const RefPtr<S, WeakPtrExtension, DeletePolicy>& rhs)
        {
            if(WeakUtils::IsEqual(*this, rhs))
                return *this;

			Delete();

            WeakUtils::Copy(*this, rhs);

            return *this;
        }

		/**
		*   Return reference to real object using proxy object operator.
        */
        T& operator*()
        {
            if(m_counter != 0 && m_pointer != 0)
                return *m_pointer;
			else
				throw std::logic_error("Not initialized m_pointer");
        }

		/**
		*   Return m_pointer to real object using proxy object operator.
        */
        T* operator->()
        {
            if(m_counter != 0 && m_pointer != 0)
                return m_pointer;
			else
				std::logic_error("Not initialized m_pointer");
			return NULL;
        }

		/**
		*   Return reference to real object using proxy object operator.
        */
        const T& operator*()const
        {
            if(m_counter == 0 || m_pointer == 0)
				std::logic_error("Not initialized m_pointer");

			return *m_pointer;
        }

		/**
		*   Return m_pointer to real object using proxy object operator.
        */
        const T* operator->()const
        {
            if(m_counter == 0 || m_pointer == 0)
				std::logic_error("Not initialized m_pointer");
			return m_pointer;
        }

		/**
		*   Return m_pointer to object
		*/
		T* GetPointer()
		{
            if(m_counter == 0 || m_pointer == 0)
				std::logic_error("Not initialized m_pointer");
			return m_pointer;
		}

		/**
		*   Return m_pointer to object
		*/
		const T* GetPointer()const
		{
            if(m_counter == 0 || m_pointer == 0)
				std::logic_error("Not initialized m_pointer");
			return m_pointer;
		}

		/**
		*   Return number of references.
        */
        int GetCount()const
		{
			if(m_counter != 0 && m_pointer != 0)
				return m_counter->GetCount();
			return 0;
		};

		/**
		*   Test m_pointer is initialized or no.
        */
        bool IsNull()const
        {
			if(m_counter != 0)
				return m_counter->GetCount() == 0;
			return true;
        }

		/**
		*   Delete value.
        *   Decrement reference, and destroy object.
        */
        void Delete()
        {
            if(m_counter != 0 && m_pointer != 0)
            {
                m_counter->DelWeakRef();
                if(m_counter->GetWeakCount() == 0 && m_counter->GetCount() == 0)
                    delete m_counter;
				m_counter = 0;
				m_pointer = 0;
            }
        }

		/**
		*   Comparison operator.
        *   Compare pointers to proxy objects.
        */
        bool operator == ( const WeakPtr<T, DeletePolicy>& ptr )
        {
            return WeakUtils::IsEqual(*this, ptr);
        }

		/**
		*   Comparison operator.
        *   Compare pointers to proxy objects.
        */
        bool operator != ( const WeakPtr<T, DeletePolicy>& ptr )
        {
            return !WeakUtils::IsEqual(*this, ptr);
        }

	private:

        Counter<WeakPtrExtension>* m_counter;
		T*       m_pointer;
    };

    /********************************************************************************************************/
	template<class S1, template<class> class DP1,class S, template<class> class DP>
	void WeakUtils::Copy(WeakPtr<S1, DP1>& ptr, const WeakPtr<S, DP>& aptr)
	{
		ptr.m_counter = aptr.m_counter;
		ptr.m_pointer = aptr.m_pointer;
		if(ptr.m_counter != 0)
			ptr.m_counter->AddWeakRef();
	}
    /********************************************************************************************************/
	template<class S1, template<class> class DP1,class S, template<class> class DP>
	void WeakUtils::Copy(WeakPtr<S1, DP1>& ptr, const RefPtr<S, WeakPtrExtension, DP>& aptr)
	{
		ptr.m_counter = aptr.m_counter;
		ptr.m_pointer = aptr.m_pointer;
		if(ptr.m_counter != 0)
			ptr.m_counter->AddWeakRef();
	}
    /********************************************************************************************************/
	template<class S1, template<class> class DP1,class S, template<class> class DP>
	void WeakUtils::UnsafeCopy(WeakPtr<S1, DP1>& ptr, const WeakPtr<S, DP>& aptr)
	{
		ptr.m_counter = aptr.m_counter;
		ptr.m_pointer = static_cast<S1*>(aptr.m_pointer);
		if(ptr.m_counter != 0)
			ptr.m_counter->AddWeakRef();
	}
    /********************************************************************************************************/
	template<class S1, template<class> class DP1,class S, template<class> class DP>
	void WeakUtils::UnsafeCopy(WeakPtr<S1, DP1>& ptr, const RefPtr<S, WeakPtrExtension, DP>& aptr)
	{
		ptr.m_counter = aptr.m_counter;
		ptr.m_pointer = static_cast<S1*>(aptr.m_pointer);
		if(ptr.m_counter != 0)
			ptr.m_counter->AddWeakRef();
	}
    /********************************************************************************************************/
	template<class S1, template<class> class DP1,class S, template<class> class DP>
	bool WeakUtils::IsEqual(const WeakPtr<S1, DP1>& ptr, const WeakPtr<S, DP>& aptr)
	{
		return (ptr.m_counter == aptr.m_counter && ptr.m_pointer == aptr.m_pointer);
	}
	/********************************************************************************************************/
	template<class S1, template<class> class DP1,class S, template<class> class DP>
	bool WeakUtils::IsEqual(const WeakPtr<S1, DP1>& ptr, const RefPtr<S, WeakPtrExtension, DP>& aptr)
	{
		return (ptr.m_counter == aptr.m_counter && ptr.m_pointer == aptr.m_pointer);
	}
    /********************************************************************************************************/
}

#endif

